/* 
 *  @module Validator
 *  Author: Alex Hu
 *
 *  Example:
 *
 *
 */


'use strict';

GML.Validator = (function(){
    
    var V = {};

    V.unpass = '';

    V.init = function(){
        var $RT = GML.RT
            , that = V
            ;

        $(document).on('click', '[role~=submit-form]', function(e){
            var $this = $(this)
                , $form = $this.closest('form')
                , yon, before, byon, $unpass, win, scrollTo
                // , params, i, item, key, value
                , data = {}
                ;

            yon = $form.data('validate')();

            if(!yon){
                if(!$form.closest('[role~=panelMD]').length){
                    $unpass = that.unpass;
                    win = window;
                    scrollTo = $unpass.offset().top - ($(window).height() - $unpass.height()) / 2;
                    if(scrollTo < 0){
                        scrollTo = 0;
                    }
                    $(win).scrollTop(scrollTo);
                }
                return false;
            }
            before = $this.data('before');
            if(before){
                byon = before({
                    'form': $form
                });
                if(byon === false){
                    return false;
                }
            }

            V.submit($this, $form);
            e.preventDefault();
        });
    }

    V.hunt = function(s){
        var $stage = s ? s : GML.RT
            , $region = $stage.is('form') ? $stage : $('form', $stage)
            // , $sheep = $('[role=validator]', $stage)
            , that = V
            , items, i, n, bean, $item, $form, $sheep
            ;

        for(i=0; i<$region.length; i++){
            items = [];
            $form = $($region[i]);
            $sheep = $('[role=validator]', $form);
            for(n=0; n<$sheep.length; n++){
                $item = $($sheep[n]);
                that.render($item);
                items.push($item);
            }
            $form.data('validate', V.validateForm(items));
        }
    }

    V.render = function(t, c){
        var TO;

        if(!c){
            c = GML.Room[t.data('room')];
        }

        var tip = c.tip
            , max = c.max
            , min = c.min
            , required = c.required
            , type = c.type
            , match = c.match
            , error = c.error
            , custom = c.custom
            ;

        if(type){
            V.type({
                'el': t,
                'type': type
            });
        }

        if(max){
            V.max({
                'el': t,
                'limit': max
            });
        }

        if(min){
            V.min({
                'el': t,
                'limit': min
            });
        }

        if(required !== undefined){
            V.required({
                'el': t,
                'error': required
            });
        }

        if(tip){
            V.tip({
                'el': t,
                'tip': tip
            });
        }

        if(match){
            V.match({
                "el": t,
                "matchTarget": match,
                "error": error
            });
        }

        if(custom){
            V.custom({
                "el": t,
                "custom": custom
            });
        }

        V.validate(t);
        V.clear(t);
        // t.data('validate', V.validate(t));
        // t.data('clear', V.clear(t));

        t.keyup(function(){
            clearTimeout(TO);
            TO = setTimeout(function(){
                t.data('validate')();
            }, 500);
        });

    }

    /* Required - Validate Method */
    V.required = function(c){
        var i, Vfield
            , $this = c.el
            , error = c.error || T(41)
            , isRadio = $this.is("input[type=radio]")
            ;

        if(isRadio){
            Vfield = $this.closest('[role=V-field]');
            Vfield.click(function(){
                required();
            });
        }

        var test = function(el){
            var val
                , yon = true
                ;

            if(isRadio){
                if(!Vfield.find("input[name=" + el.attr("name") + "]:checked").length){
                    yon = false;
                }
            }else{
                val = el.val();

                if(/^\s*$/.test(val)){
                    yon = false;
                }
            }
            return yon;
        }

        var required = function(r){
            var yon = true
                , notice = ''
                ;

            if(!test($this)){
                notice = error; //  ? error : T(41);
                yon = false;
            }
            $this.data('notice')(notice);
            return yon;
        }

        $this.data('notice', V.notice($this));

        V.addMethod($this, required);
    }

    /* Max - Validate Method */
    V.max = function(c){
        var $this = c.el
            , limitNO = c.limit
            , $remain, i, remainTip
            ;

        if(limitNO.limit){
            limitNO = limitNO.limit;
        }else{
            $remain = $('<p />',{
                'class': 'tips',
                'html': T(39) + '<em class="txt-yellow">' + limitNO + '</em>'
            });
            $remain.insertAfter($this);
            remainTip = 1;
        }

        var limit = function(r){
            var charNO = GML.countStr($this.val())
                , remainNO = limitNO - charNO
                , notice, $remainNO, yon
                ;

            if(remainNO > -1){
                notice = '';
            }else{
                notice = T(38);
            }

            if(remainTip){
                $remainNO = $remain.find('em');
                $remainNO.text(remainNO);
            }

            if(remainNO > -1){
                if(remainTip){
                    $remainNO.removeClass('txt-red');
                    $remainNO.addClass('txt-yellow');
                }
                yon = true;
            }else{
                if(remainTip){
                    $remainNO.addClass('txt-red');
                    $remainNO.removeClass('txt-yellow');
                }
                yon = false;
            }
            $this.data('notice')(notice);
            return yon;
        }

        $this.data('notice', V.notice($this));

        /*
        $this.keyup(function(){
            limit();
        });
        */

        V.addMethod($this, limit);
        V.addClear($this, limit);
    }

    /* Min - Validate Method */
    V.min = function(c){
        var $this = c.el
            , i
            ;

        var limit = function(r){
            var charNO = GML.countStr($this.val())
                , notice, yon
                ;

            if(charNO < c.limit){
                notice = T(89, c.limit);
                yon = false;
            }else{
                notice = '';
                yon = true;
            }
            $this.data('notice')(notice);
            return yon;
        }

        $this.data('notice', V.notice($this));

        /*
        $this.keyup(function(){
            limit();
        });
        */

        V.addMethod($this, limit);
        V.addClear($this, limit);
    }

    /* Type - Validate Method */
    V.type = function(c){
        var $this = c.el
            , type = c.type
            , i, reagent, notice, unvalidTip
            ;

        switch(type){
            case 'NO':
                reagent = function(t){
                    return /^\d+$/.test(t);
                    // !isNaN(parseFloat(t)) && isFinite(t);
                }
                unvalidTip = T(90);
                break;
            case 'nonNO':
                reagent = function(t){
                    return t.match(/\d+/g) ? 0 : 1;
                }
                unvalidTip = T(165);
                break;
        }

        var test = function(){
            var value = $this.val()
                , yon
                ;

            if(value){
                yon = reagent($this.val());
            }else{
                yon = true;
            }

            if(yon){
                notice = '';
            }else{
                notice = unvalidTip;
            }
            $this.data('notice')(notice);
            return yon;
        }

        $this.data('notice', V.notice($this));

        /*
        $this.keyup(function(){
            test();
        });
        */

        V.addMethod($this, test);
        V.addClear($this, test);
    }

    V.validate = function(el){
        var i, yon, methods;

        /*
        if(el.length > 1){
            el = $(el[0]);
        }
        */
        // console.log(el);
        methods = el.data('method');

        el.data('validate', (function(){
            return function(){
                var v = true;
                for(i=0; i<methods.length; i++){
                    yon = methods[i]();
                    if(!yon){
                        v = false;
                        break;
                    }
                }
                return v;
            }
        })()
        );
    }

    V.clear = function(el){
        var i
            , clearMethods = el.data('clearMethods')
            ;

        el.data('clear', 
            (function(){
                return function(){
                    el.val('');
                    for(i=0; i<clearMethods.length; i++){
                        clearMethods[i]();
                    }
                }
            })()
        );
    }

    V.validateForm = function(i){
        var items = i
            , i, yon, $item
            ;

        return function(){
            var v = true;
            V.unpass = '';
            for(i=0; i<items.length; i++){
                $item = items[i];
                if($item.length > 1){
                    $item = $($item[0]);
                }
                yon = $item.data('validate')();
                if(!yon){
                    v = false;
                    if(!V.unpass){
                        V.unpass = $item;
                    }
                }
            }
            return v;
        }
    }

    V.addMethod = function(el, f){
        var $this = el
            , methods = $this.data('method')
            ;

        if(methods){
            $this.data('method').push(f);
        }else{
            $this.data('method', [ f ]);
        }
    }

    V.addClear = function(el, f){
        var $this = el
            , clearMethods = $this.data('clearMethods')
            ;

        if(clearMethods){
            $this.data('clearMethods').push(f);
        }else{
            $this.data('clearMethods', [ f ]);
        }
    }

    /* notice for validating failed */
    V.notice = function(el){
        var notice, i
            , Vfield = el.closest('[role=V-field]')
            , noticing = function(txt){
                var text = txt
                    , notice = Vfield.data("notice")
                    , $tip, $parent
                    ;

                if(!notice){
                    notice = $('<div />',{
                        'class': 'fm-error bg-icon'
                    });
                    notice.insertAfter(Vfield);
                    Vfield.data("notice", notice);
                }
                if(text){
                    notice.text(text);
                    el.addClass('a-error');
                    notice.show();
                }else{
                    el.removeClass('a-error');
                    notice.hide();
                }
            }

        return noticing;
    }

    V.submit = function($t, $form){
        /*
        var i, key, value, item
            , data = {}
            ;
        // params = $form.serialize().replace(/\+/g,' ').split('&')
            */

        /*
        for(i=0; i<params.length; i++){
            item = params[i].split('=');
            key = item[0];
            value = item[1];
            data[key] = decodeURIComponent(value);
        }
        */
        // data['pipe'] = 'ajax';
        $.ajax({
            url: $form.attr('action'),
            type: 'post',
            // data: data,
            data: $form.serialize(),
            beforeSend: function(){
                GML.Spinner($form);
            },
            success: function(data){
                GML.Spinner($form);
                var room
                    , callback = $t.data('callback')
                    ;

                if(!callback){
                    room = GML.Room[$t.data("room")];
                    if(room){
                        callback = room.callback;
                    }
                }
                if(callback){
                    callback({
                        'form': $form,
                        'data': data
                    });
                };
            }
        });
    }

    /* input format description */
    V.tip = function(c){
        var $t = c.el
            , $tip = $t.data('tip')
            , pos, $parent
            ;

        $parent = $t.closest('[role=V-field]');
        if(!$tip){
            $tip = $(GML.Template.tube[c.tip]());
            $tip.insertAfter($parent);
            $t.data('tip', $tip);
            $tip.hide();
        }

        $t.focus(function(){
            pos = $parent.position();
            $tip.css({
                'top': pos.top - $tip.innerHeight() - 9,
                'left': ($t.outerWidth(true) - $tip.innerWidth()) + pos.left,
                'display': 'block'
            });
        });
        $t.blur(function(){
            $tip.hide();
        });
    }

    /* see if two elements are matched */
    V.match = function(c){
        var el = c.el
            , error = c.error
            , matchTarget = $(c.matchTarget)
            ;

        var matching = function(r){
            var yon = true
                , notice = ""
                ;

            if(matchTarget.val() !== el.val()){
                notice = error ? error : T(176);
                yon = false;
            }
            el.data('notice')(notice);
            return yon;
        }

        el.data('notice', V.notice(el));

        /*
        $this.keyup(function(){
            required();
        });
        */

        V.addMethod(el, matching);
    }

    /* custom validation */
    V.custom = function(c){
        var el = c.el
            , customFN = c.custom
            , error = c.error || T(178)
            // , name = el.attr("name")
            ;

        var ving = function(r){
            var notice
                , yon = customFN(el)
                ;

            if(yon){
                notice = "";
            }else{
                notice = error;
            }
            /*
            data[name] = el.val();

            $.ajax({
                url: url,
                async: false,
                data: data,
                error: function(){
                    yon = false;
                },
                success: function(d){
                    switch(d["status"]){
                        case "success":
                            // yon = true;
                            error = "";
                            break;
                        case "fail":
                            yon = false;
                            break;
                        case "error":
                            error = "error";
                            break;
                    }
                }
            });
            */
            el.data('notice')(notice);
            return yon;
        }

        el.data('notice', V.notice(el));

        V.addMethod(el, ving);
    }

    V.init();
    return V;

})();

